package com.sayice.robotconflict.game.object;

import com.sayice.robotconflict.game.Game;
import com.sayice.robotconflict.game.msg.Message;
import com.sayice.robotconflict.game.msg.data.send.GamePlayerInfo;
import com.sayice.robotconflict.game.msg.data.send.GameRobotInfo;
import com.sayice.robotconflict.game.msg.data.send.GameTowerInfo;
import com.sayice.robotconflict.game.object.Player.State;
import com.sayice.robotconflict.game.object.property.Coord;
import com.sayice.robotconflict.game.object.property.SimpleCoord;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 队伍
 *
 * @author shubing
 */
public class Team implements Comparable<Team> {
    /**
     * 游戏房间
     */
    public GameRoom room;
    
    /**
     * 能量塔血量
     */
    public int towerBlood = Game.TOWER_MAX_BLOOD;
    
    /**
     * 能量塔坐标
     */
    public final Coord towerCoord = new SimpleCoord(-1, -1);
    
    /**
     * 分值 (用于匹配)
     */
    public final int score;
    
    /**
     * 队伍玩家，元素不可变
     */
    public final Map<String, Player> players;
    
    /**
     * 队伍中存活的机器人
     */
    public final Set<Robot> robots = new ConcurrentHashMap<Robot, Boolean>(16).keySet(true);
    
    /**
     * 能量塔攻击偏移
     */
    private final Coord towerAttackOffset = new SimpleCoord(0.0, 0.0);
    
    /**
     * 能量塔攻击目标
     */
    private Object towerAttackTarget;
    
    /**
     * 虚拟玩家列表
     */
    private final List<Player> virtualPlayers = new ArrayList<>();
    
    /**
     * 已投降的玩家
     */
    private final Set<Player> surrenderedPlayers = new ConcurrentHashMap<Player, Boolean>(16).keySet(true);
    
    public Team(Map<String, Player> players) {
        if (players.isEmpty()) {
            throw new IllegalArgumentException("玩家不能为空");
        }
        this.score = players.values().stream().peek(player -> {
                    if (player.isVirtual) {
                        this.virtualPlayers.add(player);
                    }
                    player.team = this;
                    this.robots.addAll(player.robots.values());
                })
                .collect(Collectors.averagingInt(p -> p.winRate)).intValue();
        this.players = players;
    }
    
    /**
     * 获取队伍人数
     */
    public int size() {
        return players.size();
    }
    
    /**
     * 是否是虚拟的 (全人机)
     */
    public boolean isVirtual() {
        return players.size() == virtualPlayers.size();
    }
    
    /**
     * 投降玩家数量
     */
    public int surrenderedCount() {
        return surrenderedPlayers.size();
    }
    
    /**
     * 获取玩家信息
     */
    public GamePlayerInfo[] playerInfo() {
        return players.values().stream().map(GamePlayerInfo::new).toArray(GamePlayerInfo[]::new);
    }
    
    /**
     * 获取能量塔信息
     */
    public GameTowerInfo towerInfo() {
        return new GameTowerInfo(towerBlood, towerAttackOffset);
    }
    
    /**
     * 获取存活的机器人信息
     */
    public GameRobotInfo[] robotInfo() {
        return robots.stream().map(GameRobotInfo::new).toArray(GameRobotInfo[]::new);
    }
    
    /**
     * 尝试更新玩家的状态，若有玩家更新失败则尝试回滚
     *
     * <p>不同于 {@code tryUpdatePlayersState}，本方法在遇到第一个状态不符合期望的 Player 后便不再继续尝试
     *
     * @param expect 期望的玩家状态
     * @param update 更新后的玩家状态
     * @return true表示更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean weakTryUpdatePlayersState(State expect, State update) {
        return weakTryUpdatePlayersState(expect, update, ignored -> {
        });
    }
    
    /**
     * 尝试更新玩家的状态，若有玩家更新失败则尝试回滚
     *
     * <p>不同于 {@code tryUpdatePlayersState}，本方法在遇到第一个状态不符合期望的 Player 后便不再继续尝试
     *
     * @param expect       期望的玩家状态
     * @param update       更新后的玩家状态
     * @param unExpectedDo 不符合期望的玩家执行的操作
     * @return true表示更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean weakTryUpdatePlayersState(State expect, State update, Consumer<Player> unExpectedDo) {
        List<Player> updated = new ArrayList<>();
        for (Player player : players.values()) {
            if (player.state.compareAndSet(expect, update)) {
                updated.add(player);
            } else {
                unExpectedDo.accept(player);
                updated.forEach(p -> p.state.compareAndSet(update, expect));
                return false;
            }
        }
        return true;
    }
    
    /**
     * 尝试更新玩家的状态，若有玩家更新失败则尝试回滚
     *
     * <p>不同于 {@code weakTryUpdatePlayersState}，本方法会尝试所有的 Player
     *
     * @param expect 期望的玩家状态
     * @param update 更新后的玩家状态
     * @return true表示更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean tryUpdatePlayersState(State expect, State update) {
        return tryUpdatePlayersState(expect, update, ignored -> {
        });
    }
    
    /**
     * 尝试更新玩家的状态，若有玩家更新失败则尝试回滚
     *
     * <p>不同于 {@code weakTryUpdatePlayersState}，本方法会尝试所有的 Player
     *
     * @param expect     期望的玩家状态
     * @param update     更新后的玩家状态
     * @param expectedDo 符合期望的玩家执行的操作
     * @return true表示更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean tryUpdatePlayersState(State expect, State update, Consumer<Player> expectedDo) {
        List<Player> updated = new ArrayList<>();
        for (Player player : players.values()) {
            if (player.state.compareAndSet(expect, update)) {
                expectedDo.accept(player);
                updated.add(player);
            }
        }
        boolean allExpected = updated.size() == players.size();
        if (!allExpected) {
            updated.forEach(p -> p.state.compareAndSet(update, expect));
        }
        return allExpected;
    }
    
    /**
     * 更新所有符合期望的玩家的状态
     *
     * @param expect 期望的玩家状态
     * @param update 更新后的玩家状态
     * @return true表示全部玩家的 state 都符合期望并更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean updateExpectedPlayersState(State expect, State update) {
        return updateExpectedPlayersState(expect, update, ignored -> {
        });
    }
    
    /**
     * 更新所有符合期望的玩家的状态
     *
     * @param expect     期望的玩家状态
     * @param update     更新后的玩家状态
     * @param expectedDo 符合期望的玩家执行的操作
     * @return true表示全部玩家的 state 都符合期望并更新成功，false表示某个玩家的 state 与期望状态不符
     */
    public boolean updateExpectedPlayersState(State expect, State update, Consumer<Player> expectedDo) {
        boolean allUpdated = true;
        for (Player player : players.values()) {
            if (player.state.compareAndSet(expect, update)) {
                expectedDo.accept(player);
            } else {
                allUpdated = false;
            }
        }
        return allUpdated;
    }
    
    /**
     * 游戏初始化
     *
     * @param room      游戏房间
     * @param teamIndex 队伍下标
     */
    public void gameInit(GameRoom room, int teamIndex) {
        this.room = room;
        towerCoord.setX(950);
        int index = 0;
        int sep = robots.size() / 2;
        if (teamIndex == 0) {
            towerCoord.setY(900);
            int posY = 900;
            for (Robot robot : robots) {
                if (index > sep) {
                    posY = 750;
                    index = 0;
                }
                robot.coord.set(index * 100 + 50, posY);
                index++;
            }
        } else {
            towerCoord.setY(0);
            int posY = 0;
            for (Robot robot : robots) {
                if (index > sep) {
                    posY = 150;
                    index = 0;
                }
                robot.coord.set(1900 - (index * 100) - 50, posY);
                robot.direction.set(0, 1);
                index++;
            }
        }
    }
    
    /**
     * 能量塔是否与矩形盒体发生碰撞
     *
     * @param x1 矩形左上角 x 坐标
     * @param y1 矩形左上角 y 坐标
     * @param x2 矩形右下角 x 坐标
     * @param y2 矩形右下角 y 坐标
     * @return true表示发送碰撞，false表示未发生碰撞
     */
    public boolean towerCollidedWith(double x1, double y1, double x2, double y2) {
        return Game.collided(towerCoord.getX(), towerCoord.getY(), towerCoord.getX() + Game.TOWER_SIZE, towerCoord.getY() + Game.TOWER_SIZE, x1, y1, x2, y2);
    }
    
    /**
     * 切换投降状态
     *
     * @param player 玩家
     */
    public void changeSurrender(Player player) {
        boolean over = false;
        if (surrenderedPlayers.contains(player)) {
            surrenderedPlayers.remove(player);
        } else {
            surrenderedPlayers.add(player);
            // 若真实玩家全部投降，则人机自动投降，对方获胜
            if (surrenderedPlayers.size() >= players.size() - virtualPlayers.size()) {
                surrenderedPlayers.addAll(virtualPlayers);
                over = true;
            }
        }
        send(new Message("game", "surrenderedCount", surrenderedPlayers.size()));
        if (over) {
            room.winner.compareAndSet(null, room.rivalOf(this));
        }
    }
    
    /**
     * 执行一个 Tick 的操作
     */
    public void tickAction() {
        // 攻击进入能量圈内的敌方机器人
        Robot attackTarget = (Robot) towerAttackTarget;
        Team rivalTeam = room.rivalOf(this);
        if (attackTarget == null || towerCoord.sqrDistanceTo(attackTarget.coord) >= Game.SQR_TOWER_ENERGY_RADIUS) {
            attackTarget = (Robot) (towerAttackTarget = null);
            for (Robot rivalRobot : rivalTeam.robots) {
                if (towerCoord.sqrDistanceTo(rivalRobot.coord) < Game.SQR_TOWER_ENERGY_RADIUS) {
                    attackTarget = (Robot) (towerAttackTarget = rivalRobot);
                    break;
                }
            }
        }
        if (attackTarget == null) {
            towerAttackOffset.set(0, 0);
            return;
        }
        if ((attackTarget.blood -= Game.TOWER_ENERGY_VALUE) <= 0) {
            rivalTeam.robots.remove(attackTarget);
            towerAttackTarget = null;
            towerAttackOffset.set(0, 0);
            return;
        }
        towerAttackOffset.set(attackTarget.coord.getX() - towerCoord.getX(), attackTarget.coord.getY() - towerCoord.getY());
    }
    
    /**
     * 向队伍中的玩家发送消息
     *
     * @param msg 消息
     */
    public void send(Message msg) {
        players.values().forEach(player -> player.send(msg));
    }
    
    /**
     * 向队伍中除指定玩家以外的玩家发送消息
     *
     * @param msg      消息
     * @param excepted 不发送消息的玩家
     */
    public void sendExcept(Message msg, Player excepted) {
        players.values().forEach(player -> {
            if (player != excepted) {
                player.send(msg);
            }
        });
    }
    
    @Override
    public int hashCode() {
        return System.identityHashCode(this);
    }
    
    @Override
    public boolean equals(Object o) {
        return this == o;
    }
    
    @Override
    public int compareTo(Team o) {
        int c1 = Boolean.compare(isVirtual(), o.isVirtual());
        if (c1 != 0) {
            return c1;
        }
        int c2 = score - o.score;
        if (c2 != 0) {
            return c2;
        }
        Player player1 = players.values().stream().findFirst().get();
        Player player2 = o.players.values().stream().findFirst().get();
        return player1.id.compareTo(player2.id);
    }
}
